
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fonts.h"
#include <string.h>	/* for strlen */

char gszLine[LCD_MAX_CHAR+1] = {0,};

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define	_OPTIMIZE_
#define	SPI_PORT		GPIOC
#define	SPI_SCL_PIN		GPIO_Pin_10
#define	SPI_MISO_PIN	GPIO_Pin_11
#define	SPI_MOSI_PIN	GPIO_Pin_12
#define	LCD_nCS_PORT	GPIOD
#define	LCD_nCS_PIN		GPIO_Pin_2
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static sFONT *gpFont = &Font16x24;
static char gLine[LCD_MAX_CHAR+1] = {0,};
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
static void spiWrite(u8 isCmd, u8 Data);
#define	WRITE_COMMAND(_d_)	spiWrite(1, _d_)
#define	WRITE_DATA(_d_)		spiWrite(0, _d_)


void Display_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	u32 delay;

	/* GPIOC Periph clock enable */
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);

	/* SPI3 : SCL, MOSI */
	GPIO_InitStructure.GPIO_Pin = SPI_SCL_PIN | SPI_MOSI_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(SPI_PORT, &GPIO_InitStructure);
	/* SPI3 : MISO */
	GPIO_InitStructure.GPIO_Pin = SPI_MISO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(SPI_PORT, &GPIO_InitStructure);

	/* GPIOD Periph clock enable */
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
	/* LCD : nCS */
	GPIO_InitStructure.GPIO_Pin = LCD_nCS_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(LCD_nCS_PORT, &GPIO_InitStructure);


	/* Interface Mode Control */
	//-1, 0xb0, 0x0,
	WRITE_COMMAND(0xB0);
	WRITE_DATA(0x00);
	/* Sleep OUT */
	//-1, 0x11,
	WRITE_COMMAND(0x11);
	//-2, 250,
	delay = gvSysTickCount + 125;
	while (delay > gvSysTickCount);
	/* Interface Pixel Format */
	//-1, 0x3A, 0x55(16bit), 0x66(18bit)
	WRITE_COMMAND(0x3A);
	WRITE_DATA(0x66);
	/* Power Control 3 */
	//-1, 0xC2, 0x44,
	WRITE_COMMAND(0xC2);
	WRITE_DATA(0x44);
	/* VCOM Control 1 */
	//-1, 0xC5, 0x00, 0x00, 0x00, 0x00,
	WRITE_COMMAND(0xC5);
	WRITE_DATA(0x00);
	WRITE_DATA(0x00);
	WRITE_DATA(0x00);
	WRITE_DATA(0x00);
	/* PGAMCTRL(Positive Gamma Control) */
	//-1, 0xE0, 0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00,
	WRITE_COMMAND(0xE0);
	WRITE_DATA(0x0F);
	WRITE_DATA(0x1F);
	WRITE_DATA(0x1C);
	WRITE_DATA(0x0C);
	WRITE_DATA(0x0F);
	WRITE_DATA(0x08);
	WRITE_DATA(0x48);
	WRITE_DATA(0x98);
	WRITE_DATA(0x37);
	WRITE_DATA(0x0A);
	WRITE_DATA(0x13);
	WRITE_DATA(0x04);
	WRITE_DATA(0x11);
	WRITE_DATA(0x0D);
	WRITE_DATA(0x00);
	/* NGAMCTRL(Negative Gamma Control) */
	//-1, 0xE1, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00,
	WRITE_COMMAND(0xE1);
	WRITE_DATA(0x0F);
	WRITE_DATA(0x32);
	WRITE_DATA(0x2E);
	WRITE_DATA(0x0B);
	WRITE_DATA(0x0D);
	WRITE_DATA(0x05);
	WRITE_DATA(0x47);
	WRITE_DATA(0x75);
	WRITE_DATA(0x37);
	WRITE_DATA(0x06);
	WRITE_DATA(0x10);
	WRITE_DATA(0x03);
	WRITE_DATA(0x24);
	WRITE_DATA(0x20);
	WRITE_DATA(0x00);
	/* Digital Gamma Control 1 */
	//-1, 0xE2, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00,
	WRITE_COMMAND(0xE2);
	WRITE_DATA(0x0F);
	WRITE_DATA(0x32);
	WRITE_DATA(0x2E);
	WRITE_DATA(0x0B);
	WRITE_DATA(0x0D);
	WRITE_DATA(0x05);
	WRITE_DATA(0x47);
	WRITE_DATA(0x75);
	WRITE_DATA(0x37);
	WRITE_DATA(0x06);
	WRITE_DATA(0x10);
	WRITE_DATA(0x03);
	WRITE_DATA(0x24);
	WRITE_DATA(0x20);
	WRITE_DATA(0x00);
	/* Sleep OUT */
	//-1, 0x11,
	WRITE_COMMAND(0x11);

	/* Display ON */
	//-1, 0x29,
	WRITE_COMMAND(0x29);
	/* end marker */
	//-3

	/* Memory Access Control */
	// MY[7], MX[6], MV[5]
	// 000(Normal), Y-Mirror(001), X-Mirror(010), ...
	WRITE_COMMAND(0x36);
	WRITE_DATA(1<<6);

	/* Display Inversion ON */
	WRITE_COMMAND(0x21);

#if 0
	LcdDrawRect(0, 0, LCD_SCREEN_WIDTH, LCD_SCREEN_HEIGHT, COLOR_BLACK);

	LcdDrawChar(5, 5, 'R', RGB(0xFF,0x00,0x00), RGB(0x00,0x00,0x00));
	LcdDrawChar(5+LCD_CHAR_WIDTH, 5, 'G', RGB(0x00,0xFF,0x00), RGB(0xFF,0xFF,0xFF));
	LcdDrawChar(5+LCD_CHAR_WIDTH*2, 5, 'B', RGB(0x00,0x00,0xFF), RGB(0x00,0x00,0x00));

	LcdDrawRect(5, 30, 30, 30, COLOR_GREEN);
	LcdDrawLine(5, 30, 30, LCD_DIR_HORZONTAL, COLOR_YELLOW);
	LcdDrawLine(5, 30, 30, LCD_DIR_VERTICAL, COLOR_YELLOW);

	u32 time = gvSysTickCount;
	char szLine[LCD_MAX_CHAR + 1] = {0,};
	for (int i=5; i<18; i++) {
		sprintf(szLine, "%d Hello World !!!", i);
		LcdStringLine(i, szLine, COLOR_BLACK, COLOR_YELLOW);
	}
	sprintf(szLine, "time %d", gvSysTickCount - time);
	LcdStringLine(19, szLine, COLOR_BLACK, COLOR_YELLOW);
#endif
}


static void spiWrite(u8 isCmd, u8 Data)
{
#ifdef _OPTIMIZE_
	LCD_nCS_PORT->BRR = LCD_nCS_PIN;			// ncs_0

	SPI_PORT->BRR = SPI_SCL_PIN;				// sclk_0
	if (isCmd) {
		SPI_PORT->BRR = SPI_MOSI_PIN;			// command
	} else {
		SPI_PORT->BSRR = SPI_MOSI_PIN;			// data
	}
	SPI_PORT->BSRR = SPI_SCL_PIN;				// sclk_1

	for (u32 i=8; i!=0; i--) {
		SPI_PORT->BRR = SPI_SCL_PIN;			// sclk_0
		if (Data & 0x80) {
			SPI_PORT->BSRR = SPI_MOSI_PIN;		// Data_1
		} else {
			SPI_PORT->BRR = SPI_MOSI_PIN;		// Data_0
		}
		SPI_PORT->BSRR = SPI_SCL_PIN;			// sclk_1
		Data = Data << 1;
	}

	LCD_nCS_PORT->BSRR = LCD_nCS_PIN;			// ncs_1
#else
	GPIO_WriteBit(LCD_nCS_PORT, LCD_nCS_PIN, Bit_RESET);		// ncs_0

	GPIO_WriteBit(SPI_PORT, SPI_SCL_PIN, Bit_RESET);			// sclk_0
	if (isCmd) {
		GPIO_WriteBit(SPI_PORT, SPI_MOSI_PIN, Bit_RESET);		// command
	} else {
		GPIO_WriteBit(SPI_PORT, SPI_MOSI_PIN, Bit_SET);			// data
	}
	GPIO_WriteBit(SPI_PORT, SPI_SCL_PIN, Bit_SET);				// sclk_1

	for (u32 i=8; i!=0; i--) {
		GPIO_WriteBit(SPI_PORT, SPI_SCL_PIN, Bit_RESET);		// sclk_0
		if (Data & 0x80) {
			GPIO_WriteBit(SPI_PORT, SPI_MOSI_PIN, Bit_SET); 	// Data_1
		} else {
			GPIO_WriteBit(SPI_PORT, SPI_MOSI_PIN, Bit_RESET);	// Data_0
		}
		GPIO_WriteBit(SPI_PORT, SPI_SCL_PIN, Bit_SET);			// sclk_1
		Data = Data << 1;
	}

	GPIO_WriteBit(LCD_nCS_PORT, LCD_nCS_PIN, Bit_SET);			// ncs_1
#endif
}


void LcdDrawRect(u16 x, u16 y, u16 cx, u16 cy, COLORREF cr)
{
	u16 xe = x + cx - 1;
	u16 ye = y + cy - 1;

	assert_param((LCD_SCREEN_WIDTH > x));
	assert_param((LCD_SCREEN_HEIGHT > y));
	assert_param((LCD_SCREEN_WIDTH > xe));
	assert_param((LCD_SCREEN_HEIGHT > ye));

	WRITE_COMMAND(0x2A);
	WRITE_DATA(x >> 8);
	WRITE_DATA(x & 0xFF);
	WRITE_DATA(xe >> 8);
	WRITE_DATA(xe & 0xFF);

	WRITE_COMMAND(0x2B);
	WRITE_DATA(y >> 8);
	WRITE_DATA(y & 0xFF);
	WRITE_DATA(ye >> 8);
	WRITE_DATA(ye & 0xFF);

	WRITE_COMMAND(0x2C);
	for (u32 dy=0; dy<cy; dy++) {
		for (u32 dx=0; dx<cx; dx++) {
			WRITE_DATA(RGB_R(cr));
			WRITE_DATA(RGB_G(cr));
			WRITE_DATA(RGB_B(cr));
		}
	}
}
void LcdDrawLine(u16 x, u16 y, u16 len, u8 dir, COLORREF cr)
{
	if (LCD_DIR_HORZONTAL == dir) {
		LcdDrawRect(x, y, len, 1, cr);
	} else {
		LcdDrawRect(x, y, 1, len, cr);
	}
}
void LcdDrawChar(u16 x, u16 y, char ch, COLORREF crT, COLORREF crB)
{
	u16 xe = x + gpFont->Width - 1;
	
	u16 ye = y + gpFont->Height - 1;
	const u16 *data = &gpFont->table[(ch - 0x20) * gpFont->Height];
	COLORREF cr;

	WRITE_COMMAND(0x2A);
	WRITE_DATA(x >> 8);
	WRITE_DATA(x & 0xFF);
	WRITE_DATA(xe >> 8);
	WRITE_DATA(xe & 0xFF);

	WRITE_COMMAND(0x2B);
	WRITE_DATA(y >> 8);
	WRITE_DATA(y & 0xFF);
	WRITE_DATA(ye >> 8);
	WRITE_DATA(ye & 0xFF);

	WRITE_COMMAND(0x2C);
	for (u32 dy=0; dy<gpFont->Height; dy++) {
		for (u32 dx=0; dx<gpFont->Width; dx++) {
			if ((((data[dy] & ((0x80 << ((gpFont->Width/12) * 8)) >> dx)) == 0x00)
				&& (gpFont->Width <= 12)) || (((data[dy] & (0x1 << dx)) == 0x00)
				&& (gpFont->Width > 12))) {
				cr = crB;
			} else {
				cr = crT;
			}
			WRITE_DATA(RGB_R(cr));
			WRITE_DATA(RGB_G(cr));
			WRITE_DATA(RGB_B(cr));
		}
	}
}

void LcdDispChar(u8 tx, u8 ty, char ch, COLORREF crT, COLORREF crB)
{
	LcdDrawChar(tx*gpFont->Width, ty*gpFont->Height, ch, crT, crB);
}

void LcdDispString(u8 tx, u8 ty, char *str, COLORREF crT, COLORREF crB)
{
	char ch;

	while (ch = *str++) {
		if ((0x20 <= ch) && (0x7E >= ch)) {
			LcdDispChar(tx++, ty, ch, crT, crB);
			if (LCD_MAX_CHAR <= tx) {
				tx = 0;
				ty++;
				if (LCD_MAX_LINE <= ty) {
					return;
				}
			}
		}
	}
}

void LcdLineString(u8 line, u8 align, char *str, COLORREF crT, COLORREF crB)
{
	u8 tx = 0;
	int len = strlen(str);

	if (LCD_MAX_CHAR <= len) {
		len = LCD_MAX_CHAR;
		align = LCD_ALIGN_LEFT;
	}

	if (LCD_ALIGN_CENTER == align) {
		tx = (LCD_MAX_CHAR - len) / 2;
	} else if (LCD_ALIGN_RIGHT == align) {
		tx = LCD_MAX_CHAR - len;
	}

	memset(gLine, ' ', LCD_MAX_CHAR);
	for (u32 i=0; i<len; i++) {
		gLine[tx++] = str[i];
	}

	LcdDispString(0, line, gLine, crT, crB);
}

